package com.zf.rpc.transport.netty.server;

import com.zf.rpc.codec.CommonDecoder;
import com.zf.rpc.codec.CommonEncoder;
import com.zf.rpc.enumeration.RpcError;
import com.zf.rpc.exception.RpcException;
import com.zf.rpc.hook.ShutdownHook;
import com.zf.rpc.provider.ServiceProvider;
import com.zf.rpc.provider.ServiceProviderImpl;
import com.zf.rpc.registry.NacosServiceRegistry;
import com.zf.rpc.registry.ServiceRegistry;
import com.zf.rpc.serializer.CommonSerializer;
import com.zf.rpc.transport.AbstractRpcServer;
import com.zf.rpc.transport.RpcServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;

/**
 * @ProjectName: RPC-zhaofeng
 * @Package: com.zf.rpc.transport.netty.server
 * @ClassName: NettyServer
 * @Author: zf
 * @Description:
 * publishService 需要将服务保存在本地的注册表，同时注册到 Nacos 上。
 * 这里的实现是注册完一个服务后直接调用 start() 方法，这是个不太好的实现……导致一个服务端只能注册一个服务，之后可以多注册几个然后再手动调用 start() 方法。
 * @Date: 2021/5/7 20:54
 * @Version: 1.0
 */
public class NettyServer extends AbstractRpcServer {





    private CommonSerializer serializer;

    public NettyServer(String host, int port) {

        this(host,port,DEFAULT_SERIALIZER);

    }

    public NettyServer(String host, int port, Integer serializer){
        this.host = host;
        this.port = port;
        serviceRegistry = new NacosServiceRegistry();
        serviceProvider = new ServiceProviderImpl();
        this.serializer = CommonSerializer.getByCode(serializer);
        scanServices();
    }



    @Override
    public void start() {
        ShutdownHook.getShutdownHook().addClearAllHook();
        //1.创建两个LoopGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //2.基于ServerBootstrap配置EventLoopGroup
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)//设置channel类型
                    .handler(new LoggingHandler(LogLevel.INFO))//设置日志级别为info
                    .option(ChannelOption.SO_BACKLOG,256)//设置BACKLOG大小为256
                    .option(ChannelOption.SO_KEEPALIVE,true)//启动心跳机制监测
                    .childOption(ChannelOption.TCP_NODELAY,true)//设置数据包无延迟
                    //设置channel类型为SocketChannel
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            /**
                             * CommonEncoder、CommonDecoder和NettyServerHandler 分别是编码器，解码器和数据处理器。
                             * 数据从外部传入时需要解码，而传出时需要编码，类似计算机网络的分层模型，每一层向下层传递数据时都要加上该层的信息，而向上层传递时则需要对本层信息进行解码
                             */
                            //自定义编解码器
                            pipeline.addLast(new CommonEncoder(serializer));
                            pipeline.addLast(new CommonDecoder());
                            pipeline.addLast(new NettyServerHandler());
                            pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));

                        }
                    });
            //3.绑定端口和启动服务
            ChannelFuture future = serverBootstrap.bind(host,port).sync();

            //4.设置异步关闭连接
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            logger.error("启动服务器时有错误发生: ", e);
        }finally {
            //优雅退出函数设置
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}
